package model;

import lock.ConcreteLock;
import lock.Lock;

public class StepperManager extends Observable{
	private final Lock resultLock = new ConcreteLock(true);
	private final Lock criticalSection = new ConcreteLock(false);
	
	private Result result;
	private boolean done;
	private final Automaton automaton;
	
	public Automaton getAutomaton() {
		return automaton;
	}
	
	/**
	 * Constructor of {@link StepperManager}.
	 * @param automaton is the {@link Automaton} of this {@link StepperManager}.
	 * @param string is the inputString.
	 */
	public StepperManager(final Automaton automaton, final String string) {
		this.automaton = automaton;
		final Stepper firstStepper = new Stepper(this, Configuration.create(automaton.getStartState(),string, 0, EmptyOutput.getInstance()));
		new Thread(firstStepper).start();
	}
	
	/**
	 * Sends a {@link Result} to this {@link StepperManager}.
	 * Stops all other running {@link Stepper}s when the {@link Result} is a {@link RecognizedResult} and sets the {@link Result} of
	 * this {@link StepperManager} to that sent {@link RecognizedResult}.
	 * Else only sets the {@link Result} of this {@link StepperManager} to the {@link NotRecognizedResult} when no {@link Stepper}s are running.
	 * @param result is a {@link Result} that is sent by a {@link Stepper}.
	 */
	public void sendResult(final Result result){
		this.criticalSection.lock();
		result.accept(new ResultVisitor(){

			@Override
			public void visit(final NotRecognizedResult notRecognizedResult) {
				if(StepperManager.this.areObserversEmpty()){
					StepperManager.this.setResult(notRecognizedResult);
				}
			}

			@Override
			public void visit(final RecognizedResult recognizedResult) {
				StepperManager.this.setResult(recognizedResult);
				StepperManager.this.notifyObservers();
			}
		});
		this.criticalSection.unlock();
	}
	/**
	 * Sets the {@link Result} of this {@link StepperManager} and unlocks the result {@link Lock}.
	 * @param result
	 */
	private void setResult(final Result result){
		if(!this.done){
			this.done = true;
			this.result = result;
			this.resultLock.unlock();	
		}
	}

	/**
	 * Provides the {@link Result} of this {@link StepperManager}.
	 * Locks when the {@link Result} of this {@link Stepper} is not yet avaiable.
	 * @return the {@link Result} of this {@link StepperManager}.
	 */
	public Result getResult() {
		this.resultLock.lock();
		final Result result = this.result;
		this.resultLock.unlock();
		return result;
	}

	

}
